package bg.btanev.eightyfour.service.impl;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bg.btanev.eightyfour.service.PoolingService;
import bg.btanev.eightyfour.service.pooling.PoolingTask;

/**
 *
 * @author B.Tanev
 *
 */
public class PoolingServiceImpl implements PoolingService {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(PoolingServiceImpl.class);

	private static int MAX_FAILED_TASK_EXECUTIONS = 100;
	private static int INITIAL_TASK_DELAY = 10 * 60 * 1000; // 10 minutes

	private List<PoolingTask> tasksList;
	private Timer timer;

	public PoolingServiceImpl() {
	}

	public void setTasksList(List<PoolingTask> tasksList) {
		this.tasksList = tasksList;
	}

	public void init() {
		logger.debug("init() - start"); //$NON-NLS-1$

		startSheduler();

		logger.debug("init() - end"); //$NON-NLS-1$
	}

	public void destroy() {
		logger.debug("destroy() - start"); //$NON-NLS-1$

		if (timer != null) {
			stopSheduler();
		}

		logger.debug("destroy() - end"); //$NON-NLS-1$
	}

	@Override
	public void startSheduler() {
		logger.debug("startSheduler() - start"); //$NON-NLS-1$

		if (timer != null) {
			throw new IllegalStateException("Pooling scheduler already started.");
		}

		timer = new Timer(true);

		if (tasksList == null || tasksList.size() == 0) {
			logger.warn("There are no start up tasks defined."); //$NON-NLS-1$
		} else {
			for (PoolingTask poolingTask : tasksList) {
				addPoolingTask(poolingTask);
			}
		}

		logger.debug("startSheduler() - end"); //$NON-NLS-1$
	}

	@Override
	public void stopSheduler() {
		logger.debug("stopSheduler() - start"); //$NON-NLS-1$

		if (timer == null) {
			throw new IllegalStateException("Pooling scheduler not running."); //$NON-NLS-1$
		}

		timer.cancel();
		timer = null;

		logger.debug("stopSheduler() - end"); //$NON-NLS-1$
	}

	public void addPoolingTask(PoolingTask task) {
		logger.debug("addPoolingTask(PoolingTask) - start"); //$NON-NLS-1$

		Validate.notNull(task);
		logger
				.info(String
						.format(
								"addPoolingTask(PoolingTask) - sheduling task '%s' task for execution. First execution after '%d' ms. Task execution repeat aqfter '%d' ms.", task.getTaskName(), INITIAL_TASK_DELAY, task.getPoolingInterval())); //$NON-NLS-1$

		if (timer == null) {
			throw new IllegalStateException("Pooling scheduler not running."); //$NON-NLS-1$
		}

		timer.scheduleAtFixedRate(new PoolingTaskExecutor(task), INITIAL_TASK_DELAY, task.getPoolingInterval());

		logger.debug("addPoolingTask(PoolingTask) - end"); //$NON-NLS-1$
	}

	private class PoolingTaskExecutor extends TimerTask {
		private final PoolingTask task;
		private int failedExections = 0;

		public PoolingTaskExecutor(final PoolingTask task) {
			Validate.notNull(task);

			this.task = task;
		}

		@Override
		public void run() {
			logger.debug("Starting {} task...", task.getTaskName()); //$NON-NLS-1$

			long startTime = System.currentTimeMillis();

			// invoke task
			try {
				task.run();
			} catch (Exception e) {
				failedExections++;
				String message = String
						.format(
								"Failed to execute %s task with error %s! Total failure count: %d", task.getTaskName(), e.getMessage(), failedExections); //$NON-NLS-1$

				logger.error(message, e);
			}

			if (failedExections >= MAX_FAILED_TASK_EXECUTIONS) {
				// max num of task failures exceeded, going to remove the task from the list
				String message = String
						.format(
								"Maximum number (%s) of '%d' task failures occured. Going to cancel the task!", task.getTaskName(), MAX_FAILED_TASK_EXECUTIONS); //$NON-NLS-1$

				logger.error(message);
				this.cancel();
			}

			logger.info("{} task ended in {} ms!", task.getTaskName(), System.currentTimeMillis() - startTime); //$NON-NLS-1$
		}
	}
}
